from typing import Dict, List, Literal
import pandas as pd
from dolphindb import Session
from dash import Dash, html, Output, Input, State, callback, dcc,ctx,MATCH
import feffery_antd_components as fac
from ddbtools import BaseCRUD, get_table_columns, DBDf
from gypb.db import get_db
from .ddbuploader import DDBUploader

class CRUDTable:
    instance_store = {}  # 用于存储实例

    def __init__(
        self,
        name: str,
        db_path: str,
        table_name: str,
        key_cols: List[str],
        db_cols: List[str] | None = None,
        col_names: Dict[str, str] | None = None,
        editable_cols: List[str] | None | Literal["default"] = None,
        enable_add=True,
        enable_batch_upload=True,
        enable_delete=True,
        enable_modify=True,
        query=False,
    ):
        self.name = name
        self.db_path = db_path
        self.table_name = table_name
        self.key_cols = key_cols

        with get_db() as session:
            col_def: pd.DataFrame = get_table_columns(session, db_path, table_name)
        if db_cols is None:
            db_cols = col_def.index.to_list()
        self.db_cols = db_cols
        
        if col_names is None:
            col_names = col_def["comment"].fillna(col_def.index.to_series()).to_dict()
        self.col_names = [
            col_names[db_col] if db_col in col_names.keys() else db_col
            for db_col in db_cols
        ]
        if editable_cols is None:
            editable_cols = []
        self.editable_cols = editable_cols
        self.enable_add = enable_add
        self.enable_batch_upload = enable_batch_upload
        self.enable_delete = enable_delete
        self.enable_modify = enable_modify

        self.table_id = {"type": "crudtable", "index": self.name}
        self.add_button_id = {"type": "crudtable-addbutton", "index": self.name}
        self.modal_id = {"type": "crudtable-addmodal", "index": self.name}
        self.modal_form_id = {"type": "crudtable-addmodal-form", "index": self.name}
        self.batch_upload_button_id = {"type": "crudtable-batchupload-button", "index": self.name}
        self.batch_upload_modal_id = {"type": "crudtable-batchupload-modal", "index": self.name}
        self.notification_id = {"type": "crudtable-notification", "index": self.name}
        self.download_button_id = {"type": "crudtable-download-button", "index": self.name}
        self.download_id = {"type": "crudtable-download", "index": self.name}

        # 将实例存入类变量
        CRUDTable.instance_store[name] = self

    def get_cols(self):
        cols = [
            {
                "title": c,
                "dataIndex": c,
                "editable": True if c in self.editable_cols else False,
            }
            for c in self.col_names
        ]
        if any([self.enable_delete, self.enable_modify]):
            cols.extend(
                [
                    {
                        "title": "操作",
                        "dataIndex": "操作",
                        "renderOptions": {
                            "renderType": "button",
                            "renderButtonPopConfirmProps": {
                                "title": "确认执行？",
                                "okText": "确认",
                                "cancelText": "取消",
                            },
                        },
                    }
                ]
            ),
        return cols

    def get_data(
        self,
        conds=None,
    ):
        col_dict = dict(zip(self.col_names, self.db_cols))
        with get_db() as session:
            db_data = (
                BaseCRUD(self.db_path, self.table_name)
                .get(session, conds=conds)
                .to_dict("records")
            )
        data = [
            {col_name: str(row_value[db_col]) for col_name, db_col in col_dict.items()}
            for row_value in db_data
        ]
        if self.enable_modify:
            element = {
                "content": "修改",
                "type": "primary",
                "custom": "modify",
            }
            for d in data:
                elements: List | None = d.get("操作")
                if not elements:
                    elements = [element]
                    d["操作"] = elements
                else:
                    elements.append(element)
        if self.enable_delete:
            element = {
                "content": "删除",
                "type": "primary",
                "custom": "delete",
                "danger": True,
            }
            for d in data:
                elements: List | None = d.get("操作")
                if not elements:
                    elements = [element]
                    d["操作"] = elements
                else:
                    elements.append(element)
        return data

    def get_table(self):
        table = fac.AntdTable(
            id=self.table_id,
            columns=self.get_cols(),
            data=self.get_data(),
        )
        return table

    def new_element(self):
        col_dict = dict(zip(self.col_names, self.db_cols))
        button_newelement = fac.AntdButton(
            "新增数据", id=self.add_button_id, type="primary"
        )
        modal_newelement = fac.AntdModal(
            id=self.modal_id,
            title="新增数据",
            renderFooter=True,
            children=[
                fac.AntdCenter(
                    fac.AntdForm(
                        [
                            fac.AntdFormItem(fac.AntdInput(name=db_col), label=col_name)
                            for col_name, db_col in col_dict.items()
                        ],
                        labelCol={"span": 6},
                        wrapperCol={"span": 18},
                        style={"width": 400},
                        id=self.modal_form_id,
                        enableBatchControl=True,
                        values={},
                    ),
                    # fac.AntdButton(children=="新增数据",)
                )
            ],
        )
        return (button_newelement, modal_newelement)

    @property
    def layout(self):
        components = []
        table = self.get_table()
        components.append(table)
        if self.enable_add:
            add_elements = self.new_element()
        components.extend(add_elements)
        if self.enable_batch_upload:
            batch_upload_elements = self.batch_upload()
        components.extend(batch_upload_elements)
        components.append(fac.AntdButton("下载数据", id=self.download_button_id))
        components.append(dcc.Download(id=self.download_id))
        

        notification = html.Div(id=self.notification_id)
        components.append(notification)
        return html.Div(components)

    def batch_upload(self):
        button_batchupload = fac.AntdButton(
            "批量上传", id=self.batch_upload_button_id, type="primary"
        )
        def parser(raw_df: pd.DataFrame) -> pd.DataFrame:
            return raw_df.rename(columns=dict(zip(self.col_names, self.db_cols)))
        modal_batchupload = fac.AntdModal(
            id=self.batch_upload_modal_id,
            title="批量上传",
            renderFooter=True,
            children=DDBUploader(
                name=self.name,
                db_path=self.db_path,
                table_name=self.table_name,
                key_cols=self.key_cols,
                parser=parser,
                ).layout
        )
        return (button_batchupload, modal_batchupload)

@callback(
    Output({"type": "crudtable-addmodal", "index": MATCH}, "visible"),
    Input({"type": "crudtable-addbutton", "index": MATCH}, "nClicks"),
    prevent_initial_call=True,
)
def show_modal(nClicks):
    return True

@callback(
    Output({"type": "crudtable", "index": MATCH}, "data", allow_duplicate=True),
    Output({"type": "crudtable-notification", "index": MATCH}, "children", allow_duplicate=True),
    Input({"type": "crudtable-addmodal", "index": MATCH}, "okCounts"),
    State({"type": "crudtable-addmodal-form", "index": MATCH}, "values"),
    prevent_initial_call=True,
)
def handle_newelement(okCounts, values):
    if okCounts:
        crudtable_instance = CRUDTable.instance_store[ctx.triggered_id["index"]]
        crud = BaseCRUD(crudtable_instance.db_path, crudtable_instance.table_name)
        crud.key_cols = crudtable_instance.key_cols
        data = pd.Series(values).to_frame().T
        with get_db() as session:
            data = DBDf(
                session, crudtable_instance.db_path, table_name=crudtable_instance.table_name, data=data
            )
            crud.upsert(session, data)
    return crudtable_instance.get_data(), fac.AntdNotification(
        message="新增成功",
        description=f"成功新增数据",
        type="success",
    )

@callback(
    Output({"type": "crudtable", "index": MATCH}, "data", allow_duplicate=True),
    Output({"type": "crudtable-notification", "index": MATCH}, "children", allow_duplicate=True),
    Input({"type": "crudtable", "index": MATCH}, "nClicksButton"),
    [
        State({"type": "crudtable", "index": MATCH}, "clickedCustom"),
        State({"type": "crudtable", "index": MATCH}, "recentlyButtonClickedRow"),
        State({"type": "crudtable", "index": MATCH}, "data"),
    ],
    prevent_initial_call=True,
    allow_duplicate=True,
)
def handle_modify_delete(
    nClicksButton,
    clickedCustom,
    recentlyButtonClickedRow,
    data,
):
    crudtable_instance = CRUDTable.instance_store[ctx.triggered_id["index"]]
    crud = BaseCRUD(crudtable_instance.db_path, crudtable_instance.table_name)
    crud.key_cols = crudtable_instance.key_cols

    if clickedCustom == "modify":
        col_dict = dict(zip(crudtable_instance.col_names, crudtable_instance.db_cols))
        data = pd.Series(recentlyButtonClickedRow).to_frame().T
        data.rename(columns=col_dict, inplace=True)
        with get_db() as session:
            data = DBDf(
                session, crudtable_instance.db_path, table_name=crudtable_instance.table_name, data=data
            )
            crud.upsert(session, data)
        return crudtable_instance.get_data(), fac.AntdNotification(
            message="修改成功",
            description=f"成功修改字段",
            type="success",
        )
    elif clickedCustom == "delete":
        col_dict = dict(zip(crudtable_instance.db_cols, crudtable_instance.col_names))
        kws = {
            col: f"'{recentlyButtonClickedRow[col_dict[col]]}'"
            for col in crudtable_instance.key_cols
        }
        with get_db() as session:
            crud.delete(session, **kws)
        return crudtable_instance.get_data(), fac.AntdNotification(
            message="删除成功",
            description=f"成功删除字段",
            type="success",
        )
    return data, None

@callback(
    Output({"type": "crudtable-download", "index": MATCH}, "data"),
    Input({"type": "crudtable-download-button", "index": MATCH}, "nClicks"),
    State({"type": "crudtable", "index": MATCH}, "data"),
)
def download_data(nClicks, data):
    if nClicks:
        crudtable_instance = CRUDTable.instance_store[ctx.triggered_id["index"]]
        df = pd.DataFrame(data)[crudtable_instance.col_names]
        return dcc.send_data_frame(df.to_excel, f"{crudtable_instance.name}.xlsx")

@callback(
    Output({"type": "crudtable-batchupload-modal", "index": MATCH}, "visible"),
    Input({"type": "crudtable-batchupload-button", "index": MATCH}, "nClicks"),
    prevent_initial_call=True,
)
def show_batch_upload_modal(nClicks):
    return True
    
